package org.gruposp2p.proyectokarma.gwt.client.ui.document;

import java.util.HashSet;
import java.util.Set;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.DateBox;
import org.gruposp2p.proyectokarma.gwt.client.MainEntryPoint;
import org.gruposp2p.proyectokarma.gwt.client.domain.Document;
import org.gruposp2p.proyectokarma.gwt.client.json.DomainToJSON;
import org.gruposp2p.proyectokarma.gwt.client.json.JSONToDomain;
import org.gruposp2p.proyectokarma.gwt.client.ui.AddTagsDialog;
import org.gruposp2p.proyectokarma.gwt.client.ui.ErrorDialog;
import org.gruposp2p.proyectokarma.gwt.client.util.RequestHelper;
import org.gruposp2p.proyectokarma.gwt.client.util.ServerPaths;
import org.gruposp2p.proyectokarma.gwt.client.util.Validator;
import org.gruposp2p.proyectokarma.gwt.client.widget.ClickableLabel;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.json.client.JSONObject;
import com.allen_sauer.gwt.log.client.Log;

public class EditorDialog implements AddTagsDialog.Handler, 
	ClickableLabel.Handler, AddOptionsDialog.Handler {
	
	public enum Action {EDIT,CREATE}
	
    interface EditorStyle extends CssResource {
        String errorTextBox();
        String textBox();
    }
    
	private static EditorDialogUiBinder uiBinder = GWT
    	.create(EditorDialogUiBinder.class);

	interface EditorDialogUiBinder extends
		UiBinder<Widget, EditorDialog> {
	}

    @UiField DialogBox dialogBox;
    @UiField EditorStyle style;
    @UiField VerticalPanel mainPanel;
    @UiField VerticalPanel messagePanel;
    @UiField Label messageLabel;
    @UiField Button acceptButton;
    @UiField Button cancelButton;
    @UiField Button tagsButton;
    @UiField Button optionsButton;
    @UiField VerticalPanel contentPanel;
    @UiField FlowPanel selectedTagsPanel;
    @UiField VerticalPanel selectedTagsPanelContainer;
    @UiField LayoutPanel loadingPanel;
    @UiField VerticalPanel editorPanel;
    @UiField TextBox name;
    @UiField ListBox isPollingListBox;
    @UiField VerticalPanel optionsPanelContainer;
    @UiField VerticalPanel optionsPanel;
    @UiField VerticalPanel pollingDatePanel;
    @UiField DateBox pollingValidTo;

    private HTML editor = null;
    private Document documentToEdit;
    Set<String> tagSet;
    Set<String> optionSet;
    AddTagsDialog addTagsDialog = null;
    AddOptionsDialog addOptionsDialog = null;

    private static EditorDialog INSTANCE;
    
    private EditorDialog() {
        uiBinder.createAndBindUi(this);
        optionSet = new HashSet<String>();
        tagSet = new HashSet<String>();
        messagePanel.setVisible(false);
        optionsPanel.setVisible(false);
        optionsPanelContainer.setVisible(false);
        optionsButton.setVisible(false);
        contentPanel.setVisible(false);
        pollingDatePanel.setVisible(false);
        acceptButton.setStylePrimaryName("acceptButton");
        cancelButton.setStylePrimaryName("cancelButton");
        isPollingListBox.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				if (((ListBox)event.getSource()).getSelectedIndex() == 0) {
					optionsPanelContainer.setVisible(false);
					optionsButton.setVisible(false);
			        pollingDatePanel.setVisible(false);
        		} else {
        			optionsPanelContainer.setVisible(true);
        	        pollingDatePanel.setVisible(true);
        			optionsButton.setVisible(true);
        		}
			}
		});
        tagsButton.setStylePrimaryName("addButton");
        optionsButton.setStylePrimaryName("addButton");
        optionsButton.getElement().getStyle().setMarginTop(13, Unit.PX);
        tagsButton.getElement().getStyle().setMarginTop(13, Unit.PX);
        tagsButton.getElement().getStyle().setMarginRight(25, Unit.PX);
        SubmitHandler sh = new SubmitHandler();
        name.addKeyDownHandler(sh);
    }
    
    public static EditorDialog getInstance () {
    	if (INSTANCE == null) INSTANCE = new EditorDialog (); 
    	return INSTANCE;
    }

    public void show(Document document, Action action) {
    	if (document == null) this.documentToEdit = new Document();
    	else this.documentToEdit = document;
        //dialogBox.center();
        messagePanel.setVisible(false);
        selectedTagsPanelContainer.setVisible(false);
        dialogBox.setPopupPosition((Window.getClientWidth()/4), 40);
        dialogBox.show();
    	if (editor == null) {
			editor = new HTML("<textarea name=\"documentData\" width=\"500px\"></textarea>");
			editorPanel.add(editor);
			showEditor();
		    Timer t = new Timer() {
		        @Override
		        public void run() {
		            if (documentToEdit != null) {
		            	setEditorData(documentToEdit.getContent());
		            } 
	    			contentPanel.setVisible(true);
	    			loadingPanel.setVisible(false);
		        }
		      };
	        t.schedule(500);
        } else {
        	setEditorData("");
        	editorPanel.add(editor);
        }
        if (documentToEdit != null) {
        	name.setText(documentToEdit.getName());
        	setEditorData(documentToEdit.getContent());
        }
        switch (action) {
        	case EDIT:
        		dialogBox.setText("Editar documento");
        	break;
        	case CREATE:
        		dialogBox.setText("Crear documento");
        	break;	
        }
    }
	
    @UiHandler("acceptButton")
    void handleAcceptButton(ClickEvent e) {
		if (!isValidForm()) return;
		documentToEdit.setPolling(isPollingListBox.getSelectedIndex() == 1);
		documentToEdit.setName(name.getText());
		documentToEdit.setContent(getEditorData());
    	documentToEdit.setTags(tagSet);
    	documentToEdit.setOptions(optionSet);
    	documentToEdit.setPollingValidTo(pollingValidTo.getValue());
		JSONObject JSONDocument = DomainToJSON.getDocument(documentToEdit);
		RequestHelper.doPost(ServerPaths.getDocumentsPath(), 
				JSONDocument.toString(), new PostDocumentRequestCallback());
		dialogBox.hide();
		acceptButton.setEnabled(false);
    	History.back();
    }
    
    @UiHandler("cancelButton")
    void handleCancelButton(ClickEvent e) {
		if (isEditorDirty()) {
			if(Window.confirm(MainEntryPoint.INSTANCE.constants.confirmExitDirtyDocument())) {
				dialogBox.hide();
			}
		} else {
			dialogBox.hide();
		}
    	History.back();
    }
    
    @UiHandler("tagsButton")
    void handleTagsButton(ClickEvent e) {
    	if (addTagsDialog == null) addTagsDialog = new AddTagsDialog ();
    	addTagsDialog.show(this);
    }
    
    @UiHandler("optionsButton")
    void handleOptionsButton(ClickEvent e) {
    	if (addOptionsDialog == null) addOptionsDialog = new AddOptionsDialog ();
    	addOptionsDialog.show(this);
    }
    
	private boolean isValidForm() {
		if (Validator.isTextBoxEmpty(name)) {
			setMessage(MainEntryPoint.INSTANCE.constants.emptyFieldException());
			name.setStyleName(style.errorTextBox(), true);
			return false;
		} else {
			name.setStyleName(style.errorTextBox(), false);
		}
		if (!isEditorDirty()) {
			setMessage(MainEntryPoint.INSTANCE.constants.documentEmpty());
			editorPanel.setStyleName(style.errorTextBox(), true);
			return false;
		}
		return true;
	}
	
	native String getEditorData()/*-{
		return $wnd.CKEDITOR.instances.documentData.getData();
	}-*/;
	
	native String setEditorData(String editorData)/*-{
		$wnd.CKEDITOR.instances.documentData.setData(editorData);
	}-*/;
	
	native String showEditor()/*-{
		$wnd.showEditor();
	}-*/;
	
	private  boolean isEditorDirty() {
		if (!(documentToEdit.getContent().trim().equals(getEditorData().trim())) || 
				!documentToEdit.getName().trim().equals(name.getText().trim())) return true;
		else return false;
	}
    
	private void setMessage (String message) {
    	messageLabel.setText(message);
    	messagePanel.setVisible(true);
	}
	
    private class PostDocumentRequestCallback implements RequestCallback {

		public PostDocumentRequestCallback() { }

		@Override
    	public void onError(Request request, Throwable exception) {
        	new ErrorDialog().show("Exception", exception.getMessage());
        	Log.error(exception.getMessage());
        	acceptButton.setEnabled(true);
    	}

        @Override
    	public void onResponseReceived(Request request, Response response) {
    		if (response.getStatusCode() == Response.SC_CREATED) {
    			JSONValue jsonValue = JSONParser.parse(response.getText());
                Log.debug("PostDocumentRequestCallback-jsonValue: " + jsonValue);
    			Document document = JSONToDomain.getDocument(jsonValue);
    		} else {
    			new ErrorDialog().show("ERROR", "Error (" + response.getStatusCode() + "," 
    					+ response.getStatusText() + ")]");
    		}
    		acceptButton.setEnabled(true);
    	}
    }
    
    private class SubmitHandler implements KeyDownHandler {
		@Override
		public void onKeyDown(KeyDownEvent event) {
			if (KeyCodes.KEY_ENTER == event.getNativeKeyCode()) {
				acceptButton.click();
			}		
		}
	}

	private void showSelectedTags () {
    	selectedTagsPanel.clear();
    	Label tagsLabel = new Label("Etiquetas: ");
        tagsLabel.setStylePrimaryName("fieldLabel");
        selectedTagsPanel.add(tagsLabel);
    	for (String tag : tagSet) {
    		ClickableLabel tagLabel = new ClickableLabel(tag, this, ClickableLabel.Type.TAG);
    		selectedTagsPanel.add(tagLabel);
    	}
    	selectedTagsPanelContainer.setVisible((tagSet.size() > 0));
	}

	private void showSelectedOptions () {
		optionsPanel.clear();
    	Label optionsLabel = new Label("Opciones de la votación: ");
    	optionsLabel.setStylePrimaryName("fieldLabel");
    	optionsPanel.add(optionsLabel);
    	for (String option : optionSet) {
    		ClickableLabel tagLabel = new ClickableLabel(option, this, ClickableLabel.Type.OPTION);
    		optionsPanel.add(tagLabel);
    	}
    	optionsPanel.setVisible((optionSet.size() > 0));
	}
	
	@Override
	public void updateTagsPanel(Set<String> tagSet) {
		this.tagSet = tagSet;
		showSelectedTags();	
	}
	
	@Override
	public void updateOptionsPanel(Set<String> optionSet) {
		this.optionSet.addAll(optionSet);
		showSelectedOptions();	
	} 

	@Override
	public void processIconClick(ClickableLabel tagLabel) {
		switch (tagLabel.getType()) {
		case OPTION:
			optionSet.remove(tagLabel.getText());
			showSelectedOptions();	
			break;
		case TAG:
			tagSet.remove(tagLabel.getText());
			showSelectedTags();	
			break;
		}	
	}
    
}